go to index

Strings and Dictionaries

read time 13 min read
Kaggle Intro Machine Learning

Strings and Dictionaries

这一课将介绍两种基本的Python类型:Strings(字符串)Dictionaries(字典)

Strings

Python语言真正闪耀的一个地方是字符串的操作。本节将介绍一些Python内置的字符串方法和格式化操作。

这种字符串操作模式经常出现在数据科学工作的上下文中。

String syntax

在前面的课程中,你已经在示例中看到了大量的字符串,但这里回顾一下,Python中的字符串可以使用单引号或双引号定义。它们在功能上是等价的。

plaintext
x = 'Pluto is a planet'
y = "Pluto is a planet"
x == y
plaintext
True

如果字符串包含单引号字符(例如表示撇号),则双引号很方便。

类似地,如果用单引号括起来,很容易创建包含双引号的字符串:

plaintext
print("Pluto's a planet!")
print('My dog is named "Pluto"')
plaintext
Pluto's a planet!
My dog is named "Pluto"

如果把单引号放在单引号字符串中,Python会感到困惑:

plaintext
'Pluto's a planet!'
python
  File "/tmp/ipykernel_19/1561186517.py", line 1
    'Pluto's a planet!'
           ^
SyntaxError: invalid syntax

我们可以通过使用反斜杠对单引号进行“转义”来解决这个问题。

python
'Pluto\'s a planet!'
plaintext
"Pluto's a planet!"

下表总结了反斜杠字符的一些重要用法。

What you type…What you getexampleprint(example)
\'''What\'s up?'What's up?
\"""That's \"cool\""That's "cool"
\\\"Look, a mountain: /\\"Look, a mountain: /\
\n"1\n2 3"1(这里需要换行) 2 3

最后一个序列\n表示换行符。它会导致Python开始一行新代码。

python
hello = "hello\nworld"
print(hello)
plaintext
hello
world

此外,Python的三引号字符串语法允许我们从字面上包含换行符(即只需在键盘上按Enter,而不是使用特殊的\n序列)。我们已经在用来记录函数的文档字符串中看到了这一点,但我们可以在任何需要定义字符串的地方使用它们。

python
triplequoted_hello = """hello
world"""
print(triplequoted_hello)
triplequoted_hello == hello
python
hello
world

True

print()函数会自动添加换行符,除非我们给关键字参数end指定了一个值,而不是默认值’\n’:

python
print("hello")
print("world")
print("hello", end='')
print("pluto", end='')
plaintext
hello
world
hellopluto

Strings are sequences

字符串可以被认为是字符序列。可以对列表执行的操作,几乎都可以对字符串执行。

python
# Indexing
planet = 'Pluto'
planet[0]
plaintext
'P'
python
# Slicing
planet[-3:]
plaintext
'uto'
python
# How long is this string?
len(planet)
plaintext
5
python
# Yes, we can even loop over them
[char+'! ' for char in planet]
plaintext
['P! ', 'l! ', 'u! ', 't! ', 'o! ']

但它们与列表的主要区别在于,它们是不可变的。我们不能修改它们。

python
planet[0] = 'B'
# planet.append doesn't work either
python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_19/2683731249.py in <module>
----> 1 planet[0] = 'B'
      2 # planet.append doesn't work either

TypeError: 'str' object does not support item assignment

String methods

与list类似,str类型也有很多非常有用的方法。我将在这里展示几个例子。

python
# ALL CAPS
claim = "Pluto is a planet!"
claim.upper()
plaintext
'PLUTO IS A PLANET!'
python
# all lowercase
claim.lower()
plaintext
'pluto is a planet!'
python
# Searching for the first index of a substring
claim.index('plan')
plaintext
11
python
claim.startswith(planet)
plaintext
True
python
# false because of missing exclamation mark
claim.endswith('planet')
plaintext
False

Going between strings and lists: .split() and .join()

Str.split()将字符串转换为较小字符串的列表,默认情况下按空格断开。这对于从一个大字符串到单词列表非常有用。

python
words = claim.split()
words
plaintext
['Pluto', 'is', 'a', 'planet!']

偶尔你会想要按空格以外的东西进行拆分:

python
datestr = '1956-01-31'
year, month, day = datestr.split('-')

Str.join()则相反,使用调用它的字符串作为分隔符,将一个字符串列表拼接成一个长字符串。

python
'/'.join([month, day, year])
plaintext
'01/31/1956'
python
# Yes, we can put unicode characters right in our string literals :)
' 👏 '.join([word.upper() for word in words])
plaintext
'PLUTO 👏 IS 👏 A 👏 PLANET!'

Building strings with .format()

Python允许我们使用+操作符连接字符串。

python
planet + ', we miss you.'
plaintext
'Pluto, we miss you.'

如果要抛出任何非string对象,必须先小心地对它们调用str()

python
position = 9
planet + ", you'll always be the " + position + "th planet to me."
python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_19/1802981568.py in <module>
      1 position = 9
----> 2 planet + ", you'll always be the " + position + "th planet to me."

TypeError: can only concatenate str (not "int") to str
python
planet + ", you'll always be the " + str(position) + "th planet to me."
plaintext
"Pluto, you'll always be the 9th planet to me."

这很难阅读,也很讨厌打字。Str.format()来拯救它。

python
"{}, you'll always be the {}th planet to me.".format(planet, position)
plaintext
"Pluto, you'll always be the 9th planet to me."

干净多了!我们在一个“格式字符串”上调用。format(),其中要插入的Python值用占位符{}表示。

请注意,我们甚至不需要调用str()就可以将position从int转换为int。Format()替我们完成了这项工作。

如果这就是format()所做的全部工作,那么它仍然非常有用。但事实证明,它能做的远不止这些。这里只是尝鲜:

python
pluto_mass = 1.303 * 10**22
earth_mass = 5.9722 * 10**24
population = 52910390
#         2 decimal points   3 decimal points, format as percent     separate with commas
"{} weighs about {:.2} kilograms ({:.3%} of Earth's mass). It is home to {:,} Plutonians.".format(
    planet, pluto_mass, pluto_mass / earth_mass, population,
)
plaintext
"Pluto weighs about 1.3e+22 kilograms (0.218% of Earth's mass). It is home to 52,910,390 Plutonians."
python
# Referring to format() arguments by index, starting from 0
s = """Pluto's a {0}.
No, it's a {1}.
{0}!
{1}!""".format('planet', 'dwarf planet')
print(s)
plaintext
Pluto's a planet.
No, it's a dwarf planet.
planet!
dwarf planet!

关于str.format的内容,你可能可以写一本书,所以我就讲到这里,然后指向pyformat.info和官方文档进行进一步阅读。

Dictionaries

字典是一个内置的Python数据结构,用于将键映射到值。

python
numbers = {'one':1, 'two':2, 'three':3}

在这个例子中,‘one’、‘two’和’three’是keys(键),1、2和3是它们对应的value(值)

通过方括号语法访问值,类似于索引列表和字符串。

python
numbers['one']
plaintext
1

我们可以使用相同的语法添加另一个键值对

python
numbers['eleven'] = 11
numbers
plaintext
{'one': 1, 'two': 2, 'three': 3, 'eleven': 11}

或更改与现有键关联的值

python
numbers['one'] = 'Pluto'
numbers
plaintext
{'one': 'Pluto', 'two': 2, 'three': 3, 'eleven': 11}

Python提供了字典推导式(dictionary comprehension),其语法与我们在上一教程中看到的列表推导式(list comprehension)类似。

python
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
planet_to_initial = {planet: planet[0] for planet in planets}
planet_to_initial
plaintext
{'Mercury': 'M',
 'Venus': 'V',
 'Earth': 'E',
 'Mars': 'M',
 'Jupiter': 'J',
 'Saturn': 'S',
 'Uranus': 'U',
 'Neptune': 'N'}

in操作符告诉我们某个值是否是字典中的键

python
'Saturn' in planet_to_initial
plaintext
True
python
'Betelgeuse' in planet_to_initial
plaintext
False

遍历字典的for循环将遍历其键

python
for k in numbers:
    print("{} = {}".format(k, numbers[k]))
plaintext
one = Pluto
two = 2
three = 3
eleven = 11

我们可以分别使用dict.keys()和dict.values()访问所有键或所有值的集合。

python
# Get all the initials, sort them alphabetically, and put them in a space-separated string.
' '.join(sorted(planet_to_initial.values()))
plaintext
'E J M M N S U V'

非常有用的方法dict.items()让我们能够同时遍历字典的键和值。(在Python术语中,项指的是键值对。)

python
for planet, initial in planet_to_initial.items():
    print("{} begins with \"{}\"".format(planet.rjust(10), initial))
plaintext
  Mercury begins with "M"
     Venus begins with "V"
     Earth begins with "E"
      Mars begins with "M"
   Jupiter begins with "J"
    Saturn begins with "S"
    Uranus begins with "U"
   Neptune begins with "N"

要阅读字典方法的完整清单,请允许下面的代码,或查看官方在线文档

python
help(dict)

Exercise:

Question 0

让我们先从闪电开始热身。下面字符串的长度是多少?

对于下面的5个字符串,预测len()传入该字符串时将返回什么。使用可变长度来记录你的答案,然后运行单元格来检查你是否正确。

0A
python
a = ""
length = 0
q0.a.check()
0B
python
b = "it's ok"
length = 7
q0.b.check()
0C
python
c = 'it\'s ok'
length = 7
q0.c.check()
0D
python
d = """hey"""
length = 3
q0.d.check()
0E
python
e = '\n'
length = 1
q0.e.check()

Question 1

有这样一种说法:“数据科学家花80%的时间清理数据,20%的时间抱怨清理数据。”让我们看看能否编写一个函数来帮助清理我们的邮政编码数据。给定一个字符串,它应该返回该字符串是否表示有效的邮政编码。对我们来说,有效的邮政编码是由5位数字组成的任意字符串。

提示:str有一个方法在这里很有用。使用help(str)查看字符串方法列表。

python
def is_valid_zip(zip_code):
    """Returns whether the input string is a valid (5 digit) zip code
    """
    pass
    if len(zip_code) == 5:
        if(zip_code.isdigit()):
            return True
        else:
            return False
    else:
        return False
# Check your answer
q1.check()

Question 2

一位研究人员收集了数千篇新闻文章。但她想把注意力集中在包含特定单词的文章上。完成下面的功能,帮助她过滤她的文章列表。

你的函数应该满足以下条件:

  • 不要包含关键字字符串仅作为较大单词的一部分出现的文档。例如,如果她要查找关键字“closed”,你就不会包含字符串“”。
  • 她不希望你区分大写和小写字母。所以当关键字是“closed”时,短语“Closed the case.” 将被包含
  • 不要让句号或逗号影响匹配的内容。当关键字是“closed”时就短语“It is closed.”会被包含。但你可以假设没有其他类型的标点符号。
python
def word_search(doc_list, keyword):
    """
    Takes a list of documents (each document is a string) and a keyword. 
    Returns list of the index values into the original list for all documents 
    containing the keyword.

    Example:
    doc_list = ["The Learn Python Challenge Casino.", "They bought a car", "Casinoville"]
    >>> word_search(doc_list, 'casino')
    >>> [0]
    """
    ret_list = []
    keyword_1 = keyword+","
    keyword_2 = keyword+"."
    keyword_3 = " " + keyword
    keyword = " " + keyword + " "
    for i in range(len(doc_list)):
        doc_list[i] = doc_list[i].lower()
        if keyword in doc_list[i]:
            ret_list.append(doc_list.index(doc_list[i]))
        elif keyword_1 in doc_list[i]:
            ret_list.append(doc_list.index(doc_list[i]))
        elif keyword_2 in doc_list[i]:
            ret_list.append(doc_list.index(doc_list[i]))
        elif keyword_3 in doc_list[i]:
            ret_list.append(doc_list.index(doc_list[i]))
    return ret_list
        
# Check your answer
q2.check()

我好像写了一坨屎。。。。。。>_<

Question 3

现在,研究人员希望提供多个关键词进行搜索。完成下面的功能来帮助她。

(在实现这个函数时,建议你使用刚刚编写的word_search函数。以这种方式重用代码可以使程序更健壮、可读性更强——还可以节省输入!)

python
def multi_word_search(doc_list, keywords):
    """
    Takes list of documents (each document is a string) and a list of keywords.  
    Returns a dictionary where each key is a keyword, and the value is a list of indices
    (from doc_list) of the documents containing that keyword

    >>> doc_list = ["The Learn Python Challenge Casino.", "They bought a car and a casino", "Casinoville"]
    >>> keywords = ['casino', 'they']
    >>> multi_word_search(doc_list, keywords)
    {'casino': [0, 1], 'they': [1]}
    """
    # 定义一个字典用来做返回值
    ret_dic = {}
    for i in range(len(keywords)):
        ret_dic[keywords[i]] = word_search(doc_list,keywords[i])
    return ret_dic
    pass

# Check your answer
q3.check()